pub fn learn_advfeature() {
  //_learn_raw_pointers();
  //_learn_overload();
  //_learn_fully_qualified_syntax1();
  _learn_fn_pointer();
}


// 不安全rust:用于当需要舍弃rust的某些保证并负责手动维持这些保证

// 不安全的超能力
// 1,解引用裸指针
// 2,调用不安全的函数或方法
// 3,访问或修改可变静态变量
// 4,实现不安全trait
// 5,访问union字段

// unsafe并不会关闭借用检查器或禁用任何其他rust安全检查:如果在不安全代码中
// 使用引用,它仍会被检查.unsafe关键字只是提供了那5个不会被编译器检查
// 内存安全的功能

// unsafe意图作为程序员的你将会确保unsafe块中的代码以有效的方式访问内存

// 解引用裸指针
// 不安全rust有两个被称为裸指针(raw pointers)的类似于引用的新类型.
// 不可变裸指针:*const T, 和 可变裸指针: *mut T. 这里的星号不是解引用运算符;它是类型名称的一部分.
// 在裸指针的上下文中,不可变意味着指针解引用之后不能直接赋值.

// 裸指针与引用和智能指针的区别在于
// 1,允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针
// 2,不保证指向有效的内存
// 3,允许为空
// 4,不能实现任何自动清理功能

fn _learn_raw_pointers() {
  let mut num = 5;
  // 这里没有引入unsafe关键字,可以在安全代码中创建裸指针,只是不能在不安全块之外解引用裸指针.
  // 使用as将不可变和可变引用强制转为对应的裸指针,因为直接从保证安全的引用来创建它们,可以知道
  // 这些特定的裸指针是有效的,但是不能对任何裸指针做出如此假设.

  // 这里同时创建了指向相同内存位置num的不可变和可变的裸指针,如果尝试同时创建num的不可变和可变引用,
  // 则无法通过编译
  let r1 = &num as *const i32;
  let r2 = &mut num as *mut i32;

  //let r3=&num;
  //let r4=&mut num;
  // 同时使用r3和r4,编译报错,不允许同时存在不可变和可变引用
  //println!("{}, {}", r3, r4);

  // 创建一个不能确定其有效性的裸指针:一个指向任意内存地址的裸指针
  // 尝试使用任意内存是未定义行为:此地址可能有数据也可能没有,编译器可能会优化掉这个内存访问,或者
  // 程序可能会出现段错误(segmentation fault),通常不会这么做
  let address = 0x012345usize;
  let r = address as *const i32;

  // 可以在安全代码中创建裸指针,但对裸指针解引用需要在unsafe块中
  // 创建一个指针不会造成任何危险,只有当访问其指向的值时才有可能会遇到无效的值
  unsafe {
    println!("r1 is: {}, r2 is: {}", *r1, *r2);
  }
  //裸指针的主要的应用场景是调用c代码接口
}

// 调用不安全函数或方法
unsafe fn dangerous() {
  // 不安全函数体也是有效的unsafe块,所以在不安全函数中进行另一个不安全操作时无需新增额外的unsafe块
  let mut a = 3;
  let a1 = &a as *const i32;
  let a2 = &mut a as *mut i32;
  println!("{},{}", *a1, *a2);
}
fn _learn_call_unsafefn() {
  unsafe {
    dangerous();
  }
}

// 创建不安全代码的安全抽象
fn _learn_unsafe_abstract() {
  let mut v = vec![1, 2, 3, 4, 5, 6];
  let r = &mut v[..];
  // 参考split_at_mut实现
  let (a, b) = r.split_at_mut(3);
  assert_eq!(a, &mut [1, 2, 3]);
  assert_eq!(b, &mut [4, 5, 6]);
}
// 当我们知道某些事是可以的而rust不知道的时候,就触及到不安全代码了.

// 使用extern函数调用外部代码
// extern 外部函数接口(Foreign Function Interface, FFI)

// 访问或修改可变静态变量
static mut CONTER: u32 = 0;
// 不可变静态变量
static HELLO_WORLD: &str = "hello world";
// 常量与不可变静态变量的一个微妙的区别是静态变量中的值有一个固定的内存地址.使用这个值总是会访问
// 相同的地址.另一方面,常量则允许在任何被用到的时候复制其数据.另一个区别是静态变量可以是可变的.
// 访问或修改可变静态变量都是不安全的
fn _learn_global_var() {
  unsafe {
    CONTER += 1;
    println!("{}", CONTER);
  }
  // 任何可能的情况,优先使用并发技术和线程安全智能指针,这样编译器就能检测不同线程间的数据访问是否安全
}

// 实现不安全trait
// 当trait中至少有一个方法中包含编译器无法验证的不变式(invariant)时trait是不安全的.
// 可以在trait之前增加unsafe关键字将trait声明为unsafe,同时trait的实现也必须标记为unsafe
unsafe trait Foo {}
unsafe impl Foo for i32 {}

// 访问联合体中的字段
// union和struct类似,但是在一个实例中同时只能使用一个声明的字段.联合体主要用于和C代码中的联合体交互.

// 高级trait: 与trait相关的关联类型,默认类型参数,完全限定语法(fully qualified syntax),
// 关联类型在trait定义中指定占位符类型
// 关联类型是一个将类型占位符与trait相关联的方式,这样trait的方法签名中就可以使用这些占位符类型.trait
// 的实现者会针对特定的实现在这个占位符类型指定相应的具体类型.如此可以定义一个使用多种类型的trait,直到
// 实现此trait时都无需知道这些类型具体是什么.

// 关联类型也会成为trait契约的一部分:trait的实现必须提供一个类型来替代关联类型占位符.关联类型通常有
// 一个描述类型用途的名字,并且在API文档中为关联类型编写文档是一个最佳实践.

// 默认泛型类型参数和运算符重载
// 当使用泛型类型参数时,可以为泛型指定一个默认的具体类型.如果默认类型就足够的话,这消除了为具体类型实现
// trait的需要.为泛型类型指定默认类型的语法是在声明泛型类型时使用:
// <PlaceHolderType=ConcreteType>

// 这种情况的一个非常好的例子是使用运算符重载
// rust并不允许创建自定义运算符或重载任意运算符,不过std::ops中所列出的运算符和相应的trait可以通过实现
// 运算符相关trait来重载

use core::fmt;
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
  x: i32,
  y: i32,
}
// Add的实现:
// trait Add<Rhs=Self>{
//   type Output;
//   fn add(self, rhs:Rhs)->Self::Output;
// }
// Rhs=Self: 默认类型参数. Rhs是一个泛型类型参数("right hand side"的缩写),它用于定义add方法中的rhs
// 参数.如果实现Add trait时不指定Rhs的具体类型,Rhs的类型将是默认的Self类型,也就是在其上实现Add的类型.
impl Add for Point {
  type Output = Point;
  fn add(self, other: Point) -> Point {
    Point {
      x: self.x + other.x,
      y: self.y + other.y,
    }
  }
}

fn _learn_overload() {
  assert_eq!(
    Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
    Point { x: 3, y: 3 }
  );
}

// 这里有两个存放不同单元值的结构体,Millimeters和Meters.
// 这种将现有类型简单封装进另一个结构体的方式被称为newtype模式. 我们希望能够将毫米值与米值相加,并让
// Add的实现正确处理转换.可以为Millimeters实现Add并以Meters作为Rhs.
struct Millimeters(u32);
struct Meters(u32);

// 为了使Millimeters和Meters能够相加,我们指定impl Add<Meters>来设定Rhs类型参数的值而不是使用
// 默认的Self
impl Add<Meters> for Millimeters {
    type Output=Millimeters;
    fn add(self, other:Meters)->Millimeters{
        Millimeters(self.0+(other.0*1000))
    }
}
// 默认参数类型主要用于如下两个方面:
// 扩展类型而不破坏现有代码
// 在大部分用户都不需要的特定情况进行自定义

// 完全限定语法与消除歧义:调用相同名称的方法
trait Pilot{
    fn fly(&self);
}
trait Wizard{
    fn fly(&self);
}
struct Human;
impl Pilot for Human {
    fn fly(&self){
        println!("pilot human");
    }
}
impl Wizard for Human {
    fn fly(&self){
        println!("wizard human");
    }
}
impl Human{
    fn fly(&self){
        println!("only human");
    }
}
fn _learn_fully_qualified_syntax0(){
    let person = Human;
    person.fly();

    Human::fly(&person); // 等价于person.fly()
    Pilot::fly(&person);
    Wizard::fly(&person);
}

trait Animal{
    fn baby_name()->String;
}
struct Dog;
impl Dog{
    fn baby_name()->String{
        String::from("Spot")
    }
}
impl Animal for Dog{
    fn baby_name()->String {
        String::from("puppy")
    }
}
fn _learn_fully_qualified_syntax1(){
    // 直接调用定义于Dog之上的关联函数
    println!("{}", Dog::baby_name());
    // 尝试调用Dog上Animal实现的关联函数,但会编译报错,因为rust并不知道该用哪个实现
    // 因为Animal::baby_name没有self参数,同时这可能会有其他类型实现了Animal trait,rust无法计算出
    // 所需的是哪一个Animal::baby_name实现
    //println!("{}", Animal::baby_name());
    // 为了消除歧义并告诉Rust我们希望使用的是Dog的Animal实现,需要使用完全限定语法,这是调用函数时最
    // 为明确的方式
    // 在尖括号中向rust提供了类型注解,并通过在此函数调用中将Dog类型当做Animal对待,来指定希望调用的是
    // Dog上Animal trait实现中的baby_name函数.
    println!("{}", <Dog as Animal>::baby_name());
    // 通常,完全限定语法定义为,对于不是方法的关联函数,其没有receiver
    // <Type as Trait>::function(receiver_if_method, next_arg, ...);
}

// 超(父)trait(supertraits)和newtype模式
// 父trait用于在另一个trait中使用某trait的功能
// 有时我们可能会需要编写一个依赖另一个trait的trait定义:对于一个实现了第一个trait的类型,你希望要求这个
// 类型也实现了第二个trait. 如此就可以使trait定义使用第二个trait的关联项.这个所需的trait是我们实现trait
// 的父(超)trait.
// OutlinePrint只能用于同时也实现了Display并提供了OutlinePrint需要的功能的类型
trait OutlinePrint: fmt::Display {
    fn outline_print(&self){
        let output = self.to_string();
        let len=output.len();
        println!("{}", "*".repeat(len+4));
        println!("*{}*", " ".repeat(len+2));
        println!("* {} *", output);
        println!("*{}*", " ".repeat(len+2));
        println!("{}", "*".repeat(len+4));
    }
}
impl OutlinePrint for Point{}
impl std::fmt::Display for Point{
    fn fmt(&self, f:&mut fmt::Formatter)->fmt::Result{
        write!(f, "({},{})", self.x,self.y)
    }
}

// newtype模式用以在外部类型上实现外部trait
// 孤儿规则:只要trait或类型对于当前crate是本地的话就可以在此类型上实现该trait.
// 一个绕开此限制的方法是使用newtype模式,它涉及到在一个元组结构体中创建一个新类型.这个元组结构体带有一个
// 字段作为希望实现trait的类型的简单封装. 且这个封装类型对于crate是本地的,这样就可以在这个封装上实现trait.
// 例子,想要在Vec<T>上实现Display,而孤儿规则阻止我们这么做,因为Display trait和Vec<T>都定义于我们的crate
// 之外.
// 这是一个newtype
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper{
    fn fmt(&self, f:&mut fmt::Formatter)->fmt::Result{
        write!(f,"[{}]", self.0.join(", "))
    }
}
fn _learn_orphan_rule(){
    let w = Wrapper(vec![String::from("hello"), String::from("world")]);
    println!("w={}", w);
}

// 高级类型: 关于newtype模式的更多内容,类型别名,never类型和动态大小类型
// 为了类型安全和抽象而使用newtype模式
// newtype的其他功能,包括静态的确保某值不被混淆,和用来表示一个值的单位.
// 如上述的Millimeters和Meters结构体都在newtype中封装了u32值,如果编写了一个有Millimeters类型参数的函数,
// 不小心使用了Meters或普通的u32值来调用该函数的程序是不能编译的.

// newtype模式也可以用于抽象掉一些类型的实现细节:例如,封装类型可以暴露出与直接
// 使用其内部私有类型时所不同的公有API.

// newtype也可以隐藏其内部的泛型类型.例如,可以提供一个封装了HashMap<i32,String>
// 的People类型,用来储存人名以及相应的ID.使用People的代码只需要与提供的公有API交互即可.
// 比如向People集合增加名字字符串的方法,这样这些代码就无需知道在内部我们将一个i32 ID赋给了这个
// 名字了.
// newtype模式是一种实现 隐藏实现细节的封装的轻量级方法.

// 类型别名用来创建类型同义词
fn _learn_type_alias(){
    // 创建i32的别名Kilometers
    type Kilometers = i32;
    // 不同于newtype,如之前的Millimeters和Meters类型.Kilometers不是一个新的,单独的类型.Kilometers的值
    // 将被完全当做i32类型来对待.

    let x:i32=5;
    let y:Kilometers=5;
    println!("x+y={}", x+y);
    // 因为Kilometers是i32的别名,它们是同一类型,可以将i32与Kilometers相加,也可以将Kilometers传递给获取i32
    // 参数的函数.但通过这种手段无法获得newtype模式所提供的类型检查的好处.换句话说,如果在哪里混用Kilometers
    // 和i32的值,编译器不会给出错误.
    // 类型别名的主要用途是减少重复.例如,可能会有这样很长的类型:
    type Thunk = Box<dyn Fn()+Send+'static>;
    // 类型别名通过减少项目中重复代码的数量来使其更加易于控制.
}

// 类型别名也经常与Result<T,E>结合使用来减少重复.
// 如标准库里的std::io模块
/*
pub trait Write{
    fn write(&mut self, buf: &[u8])->Result<usize,Error>;
    fn flush(&mut self)->Result<(),Error>;
    fn write_all(&mut self, buf: &[u8])->Result<(),Error>;
    fn write_fmt(&mut self, fmt: fmt::Arguments)->Result<(),Error>;
}
*/
/*
这里有很多Result<...,Error>,因此,std::io有这个类型别名声明:
type Result<T>=std::result::Result<T,std::io::Error>;
因为这位于std::io中,可用的完全限定的别名是std::io::Result<T>,也就是说,Result<T,E>中E放入了
std::io::Error. Write trait最终看起来是这样

pub trait Write{
    fn write(&mut self, buf: &[u8])->Result<usize>;
    fn flush(&mut self)->Result<()>;
    fn write_all(&mut self, buf: &[u8])->Result<()>;
    fn write_fmt(&mut self, fmt: fmt::Arguments)->Result<()>;
}
 */

// 从不返回的never type
// rust有一个叫做 ! 的特殊类型.在类型理论术语中,它被称为empty type,因为它没有值.我们更倾向与称之为
// never type.这个名字描述了它的作用: 在函数从不返回的时候充当返回值.
/* 
函数bar从不返回
fn bar()->!{
}
从不返回的函数被称为 发散函数. 不能创建 ! 类型的值,所以bar也不可能返回值
*/
/*
let guess:u32=match guess.trim().parse(){
    Ok(num)=>num,
    Err(_)=>continue,
}
// match的分支必须返回相同的类型,而这里的guess必须即是整型也是continue,而rust要求guess只能是一个类型.
// continue返回的值是 !. 也就是说,当rust要计算guess的类型时,它查看这两个分支.前者是u32值,而后者是!值.
// 因为 ! 并没有一个值,rust决定guess的类型是u32.
// 描述 ！的行为的正式方式是never type可以强转为任何其他类型.允许match的分支以continue结束是因为continue
// 并不真正返回一个值;相反它把控制权交回上层循环,所以在Err的情况,事实上并为对guess赋值.
*/

// never type的另一个用途是panic!.
// 最后一个有着 ! 类型的表达式是loop
/*
print!("forever");
loop {
    print!("and ever ")
}
 */

// 动态大小类型和Sized trait
// rust需要知道有关类型的某些细节,例如为特定类型的值需要分配多少空间.
// 动态大小类型(dynamically sized types). 也叫 "DST" 或 "unsized types".
// 这些类型允许我们处理只有在运行时才知道大小的类型

// 一个典型的动态大小类型: str
fn _learn_dynamic_sized_types(){
    // rust需要知道应该为特定类型的值分配多少内存,同时所有同一类型的值必须使用相同数量的内存.
    // 如果下面两行允许编译,就是说这两个str要占用完全相同大小的空间,但它们的实际长度并不一样.
    // 这就是为什么不可能创建一个存放动态大小类型的变量的原因
    //let s1:str="hello";
    //let s2:str="hi";
    // 解决办法就是使用&str, &str储存了两个值:str的地址和其长度. 这样,&str就有了一个在编译时
    // 可以知道的大小:它是uszie长度的两倍.
    // 这是动态大小类型的常规用法:它们有一些额外的元信息来储存动态信息的大小.
    // 这引出了动态大小类型的黄金规则: 必须将动态大小类型的值置于某种指针之后.
    // 可以将str与所有类型的指针相结合: 如Box<str>或Rc<str>. 之前有过类似的,trait.
    // 每一个trait都是一个可以通过trait名称来引用的动态大小类型.为了将trait用于trait对象,必须
    // 将它们放入指针之后,如&dyn Trait或Box<dyn trait>(Rc<dyn Trait>也行)
    // 为了处理DST,rust提供了Sized trait来决定一个类型的大小是否在编译时可知.这个trait自动为编译器
    // 在编译时就知道大小的类型实现.另外,rust隐式的为每个泛型函数增加了Sized bound.也就是说,对于
    // 如下泛型函数定义:
    fn generic<T>(t:T){}
    // 实际上被当做如下处理:
    // fn generic<T:Sized>(t:T){}
    // 泛型函数默认只能用于在编译时已知大小的类型.然而可以使用如下特殊语法来放宽这个限制:
    // fn generic<T:?Sized>(t:&T){}
    // ?Sized 上的trait bound意味着"T可能是也可能不是Sized"同时这个注解会覆盖泛型类型必须在编译时
    // 拥有固定大小的默认规则.这种意义的?Trait语法只能用于Sized,而不能用于任何其他trait.
    // 另外注意我们将t参数的类型从T变为了&T: 因为其类型可能不是Sized的,所以需要将其置于某种指针之后.
}


// 高级函数和闭包: 函数指针和返回闭包

// 函数指针
// 与闭包不同,fn(函数指针,function pointer)是一个类型而不是一个trait,所以直接指定fn作为参数而不是声明
// 一个带有Fn作为trait bound的泛型参数
// 函数指针实现了所有三个闭包trait(Fn,FnMut和FnOnce),所以总是可以在调用期望闭包的函数时传递函数指针作为
// 参数.倾向于编写使用泛型和闭包trait的函数,这样它就能接受函数或闭包作为参数
// 一个只期望接受fn而不接受闭包的情况是与不存在闭包的外部代码交互时:C语言的函数可以接受函数作为参数,但是C
// 没有闭包
fn add_one(x:i32)->i32{
    x+1
}
fn do_twice(f:fn(i32)->i32, arg:i32)->i32{
    f(arg)+f(arg)
}
fn _learn_fn_pointer(){
  let answer=do_twice(add_one, 5);
  println!("answer:{}", answer);
}
//作为一个既可以使用内联定义的闭包又可以使用命名函数的例子,让我们看看一个map的应用.
// 使用map函数将一个数字vector转换为一个字符串vector,就可以使用闭包
fn _learn_fn_pointer1(){
    let list_of_numbers = vec![1,2,3];
    let list_of_strings:Vec<String>=list_of_numbers.iter().map(|i|i.to_string()).collect();
    // 或者可以将函数作为map的参数来替代闭包, 这里必须使用完全限定语法,因为存在多个叫做to_string的函数
    // 这里使用定义于ToString trait的to_string函数,标准库为所有实现了Display的类型实现了这个trait
    let list_of_strings2: Vec<String>=list_of_numbers.iter().map(ToString::to_string).collect();

    // 每一个我们定义的枚举成员的名字也变成了一个构建枚举的实例的函数
    // 作为定义枚举的结果，这些构造函数会自动被定义
    enum Status{
        Value(u32),
        Stop,
    }
    // 这里,我们可以使用枚举自动定义的构造函数作为实现了闭包trait的函数指针,即可以指定构造函数作为接受
    // 闭包的方法的参数
    let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
}

// 返回闭包
// 闭包表现为trait,这意味着不能直接返回闭包.对于大部分需要返回trait的情况,可以使用实现了期望返回的trait的
// 具体类型来替代函数的返回值.但是这不能用于闭包,因为它们没有一个可以返回的具体类型.例如不允许使用函数指针
// 作为返回值类型
// rust并不知道需要多少空间来存储闭包,这种情况可以使用trait对象来解决
// return type cannot have an unboxed trait object doesn't have a size known at compile-time
/*
fn returns_closure()->dyn Fn(i32)->i32{
    |x|x+1
}
*/

fn returns_closure()->Box<dyn Fn(i32)->i32>{
    Box::new(|x|x+1)
}

// 宏:定义在编译时定义更多代码的方式
// 宏(Macro)指的是rust中一系列的功能:使用macro_rules!的声明(Declarative)宏,和三种过程(Procedural)宏
// 1,自定义 #[derive] 宏在结构体和枚举上指定通过derive属性添加的代码
// 2,类属性(Attribute-like) 宏定义可用于任意项的自定义属性
// 3,类函数宏看起来像函数不过作用于作为参数传递的token

// 宏和函数的区别
// 从根本上来说,宏是一种为写其他代码而写代码的方式,即所谓的元编程(metaprogramming).
// 所有的这些宏以展开的方式来生成比你所手写出的更多的代码
// 元编程对于减少大量编写和维护的代码是非常有用的,它也扮演了函数扮演的角色.但宏有一些函数所没有的附加能力.

// 一个函数签名必须声明函数参数个数和类型.相比之下,宏能够接收不同数量的参数:用一个参数调用println!("hello")
// 或用两个参数调用println!("hello {}", name).而且,宏可以在编译器翻译代码前展开,例如,宏可以在一个给定类型
// 上实现trait. 而函数则不行, 因为函数是在运行时被调用,同时trait需要在编译时实现.

// 实现宏不如实现函数的一面是宏定义要比函数定义更复杂,因为你正在编写生成rust代码的rust代码.
// 由于这样的间接性,宏定义通常要比函数定义更难阅读,理解以及维护

// 宏和函数的最后一个重要区别是:在一个文件里调用宏之前必须定义它,或将其引入作用域,而函数则可以在任何地方
// 定义或调用

// 使用macro_rules!的声明宏用于通用元编程
// rust最常用的宏形式就是声明宏(declarative macros).它们有时也被称为"macros by example","macro_rules!宏",
// 或者就是"macros".其核心概念是,声明宏允许我们编写一些类似rust match表达式的代码.
// 宏也将一个值和包含相关代码的模式进行比较;此种情况下,该值是传递给宏的rust源代码的字面值,模式用于和前面
// 提到的源代码字面值进行比较,每个模式的相关代码会替换传递给宏的代码.所有这一切都发生于编译时.

// 可以使用macro_rules!来定义宏.让我们通过查看vec!宏定义来探索如何使用macro_rules!结构.
fn _learn_macro_rules0(){
    let v:Vec<u32> = vec![1,2,3];
    /*
    一个vec!定义的简化版本
    #[macro_export]
    macro_rules! vec {
        ( $( $x:expr ),* ) => {
            {
                let mut temp_vec = Vec::new();
                $(
                    temp_vec.push($x);
                )*
                temp_vec
            }
        };
    }
    #[macro_export]注解表明只要导入了定义这个宏的crate,该宏就应该是可用的.如果没有该注解,这个宏不能
    被引入作用域.
    接着使用macro_rules! 和宏名称开始宏定义,且所定义的宏并不带感叹号.名字后跟大括号表示宏定义体.
    vec!宏的结构和match表达式的结构类似.此处有一个分支模式($(x:expr),*),后跟 => 以及和模式相关的代码块.
    如果模式匹配,该相关代码块将被执行. 这里这个宏只有一个模式,拿就只有一个有效匹配方向,其他任何模式方向
    (即不匹配这个模式)都会导致错误.更复杂的宏会有多个分支模式

    宏定义中有效模式语法和之前提及的模式语法是不同的,因为宏模式锁匹配的是rust代码结构而不是值.

    首先,一对括号包含了整个模式.我们使用美元符号($)在宏系统中声明一个变量来包含匹配该模式的rust代码.
    美元符号明确表明这是一个宏变量而不是普通rust变量.之后是一对括号,其捕获了符合括号内模式的值用以在
    替代代码中使用. $()内则是$x:expr,其匹配rust的任意表达式,并将该表达式命名为$x.
    $()之后的逗号说明一个可有可无的逗号分隔符可以出现在$()所匹配的代码之后.紧随逗号之后的*说明
    该模式匹配0个或多个*之前的任何模式
    当以vec![1,2,3];调用宏时, $x模式与三个表达式1,2,3进行了三次匹配
    现在让我们来看看与此分支模式相关联的代码块中的模式:
        匹配到模式中的$()的每一部分, 都会在(=>右侧) $()* 里生成temp_vec.push($x),生成0次还是多次
    取决于模式匹配到多少次. $x 由每个与之相匹配的表达式所替换. 当以vec![1,2,3]; 调用宏时,替换该宏
    所生成的代码会是下面这样:
    {
        let mut temp_vec=Vec::new();
        temp_vec.push(1);
        temp_vec.push(2);
        temp_vec.push(3);
        temp_vec
    }
*/
}

// 用于从属性生成代码的过程宏
// 第二种形式的宏被称为过程宏(procedural macros), 因为它们更像函数(一种过程类型).过程宏接收rust代码
// 作为输入,在这些代码上进行操作,然后产生另一些代码作为输出,而非像声明式宏那样匹配对应模式然后以另一
// 部分代码替换当前代码.有三种类型的过程宏(自定义派生(derive),类属性和类函数),不过它们的工作方式都类似.

// 创建过程宏时,其定义必须驻留在它们自己的具有特殊crate类型的crate中.这么做处于复杂的技术原因,将来我们希望
// 能够消除这些限制
// use proc_macro;
// some_attribute是一个使用特定宏变体的占位符
/*
#[some_attribute]
pub fn some_name(input:TokenStream)->TokenStream{}
*/

// 如何编写自定义的derive宏
